package cn.cdeden.aiqa.service.impl;

import cn.cdeden.aiqa.domain.bo.*;
import cn.cdeden.aiqa.domain.vo.*;
import cn.cdeden.aiqa.service.DifyService;
import cn.cdeden.aiqa.service.ISysKnowledgeConfigService;
import cn.cdeden.common.core.exception.ServiceException;
import cn.cdeden.common.core.utils.OkHttp3Utils;
import cn.cdeden.common.core.utils.StringUtils;
import cn.cdeden.common.core.utils.file.FileUtils;
import cn.cdeden.common.json.utils.JsonUtils;
import cn.cdeden.resource.api.RemoteFileService;
import cn.cdeden.resource.api.domain.RemoteFile;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.utils.URIBuilder;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientRequestException;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Flux;

import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@RequiredArgsConstructor
@Service
public class DifyServiceImpl implements DifyService {

    private final ISysKnowledgeConfigService iSysKnowledgeConfigService;
    private final WebClient webClient;

    private final RemoteFileService remoteFileService;

    private static final String KNOWLEDGE_BASE_STATE = "knowledge_base_state";
    private static final String KNOWLEDGE_BASE_URL = "knowledge_base_url";
    private static final String KNOWLEDGE_BASE_KEY = "knowledge_base_key";


    private void checkKnowledgeBaseState() {
        String knowBaseState = iSysKnowledgeConfigService.getConfigValueByKey(KNOWLEDGE_BASE_STATE);
        if (!"dify".equals(knowBaseState)) {
            throw new ServiceException("知识库没有绑定Dify或者未开启");
        }
    }

    private String getKnowledgeBaseUrl() {
        return iSysKnowledgeConfigService.getConfigValueByKey(KNOWLEDGE_BASE_URL);
    }

    private String getKnowledgeBaseKey() {
        return iSysKnowledgeConfigService.getConfigValueByKey(KNOWLEDGE_BASE_KEY);
    }

    private Map<String, String> getAuthorizationHeaders() {
        String knowBaseKey = getKnowledgeBaseKey();
        HashMap<String,String> map = new HashMap<>();
        map.put("Authorization", "Bearer " + knowBaseKey);
        return map;
    }

    private String buildRequestUrl(String path, Map<String, String> params) {
        try {
            URIBuilder uriBuilder = new URIBuilder(getKnowledgeBaseUrl() + path);
            if (params != null) {
                params.forEach(uriBuilder::addParameter);
            }
            return uriBuilder.build().toString();
        } catch (URISyntaxException e) {
            throw new ServiceException("构建请求URL失败");
        }
    }

    private <T> T sendGetRequest(String url, Map<String, String> headers, Class<T> responseType) {
        try {
            String responseJson = OkHttp3Utils.get(url, headers);
            return JsonUtils.parseObject(responseJson, responseType);
        } catch (IOException e) {
            log.error("网络或IO错误，提交数据失败", e);
            throw new ServiceException("网络或IO错误，获取数据失败");
        }
    }

    private <T> T sendPostRequest(String url, String jsonBody, Map<String, String> headers, Class<T> responseType) {
        try {
            String responseJson = OkHttp3Utils.postJson(url, jsonBody, headers);
            return JsonUtils.parseObject(responseJson, responseType);
        } catch (IOException e) {
            log.error("知识库名称重复或者网络错误，提交数据失败", e);
            throw new ServiceException("知识库名称重复或者网络错误，提交数据失败");
        }
    }

    private <T> T sendPostRequestWithFile(String url, String jsonString, MultipartFile file, Map<String, String> headers, Class<T> responseType) {
        try {
            log.error("originalFileName:"+file.getOriginalFilename());
            log.error("fileName:"+(file.getName()));
            // 构建表单数据，包含 JSON 和 文件
            Map<String, Object> formData = new HashMap<>();
            formData.put("data", jsonString);  // JSON 数据
            formData.put("file", file);        // 文件

            // 调用 OkHttp3Utils 工具类的 postMultipart 方法发送请求
            String responseJson = OkHttp3Utils.postMultipart(url, headers, formData);

            // 解析响应并返回
            return JsonUtils.parseObject(responseJson, responseType);
        } catch (IOException e) {
            log.error("上传文件名称重复或网络错误，提交数据失败", e);
            throw new ServiceException("上传文件名称重复或网络错误，提交数据失败");
        }
    }


    private void sendDeleteRequest(String url, Map<String, String> headers) {
        try {
            OkHttp3Utils.delete(url, headers);
        } catch (IOException e) {
            log.error("网络或IO错误，提交数据失败", e);
            throw new ServiceException("网络或IO错误，提交数据失败");
        }
    }

    @Override
    public DifyKnowledgeBaseVo datasets(Integer page, Integer limit) {
        checkKnowledgeBaseState();

        // 构建请求参数
        Map<String, String> params = new HashMap<>();
        params.put("page", String.valueOf(page));

        // 只有在 limit 有效时才添加 limit 参数
        if (limit != null) {
            params.put("limit", String.valueOf(limit));
        }

        String url = buildRequestUrl("/v1/datasets", params);
        Map<String, String> headers = getAuthorizationHeaders();
        return sendGetRequest(url, headers, DifyKnowledgeBaseVo.class);
    }

    @Override
    public DifyKnowledgeBaseCreateVo create(DifyKnowledgeBaseBo bo) {
        checkKnowledgeBaseState();

        String url = buildRequestUrl("/v1/datasets", null);
        Map<String, String> headers = getAuthorizationHeaders();
        String jsonString = org.apache.dubbo.common.utils.JsonUtils.toJson(bo);
        return sendPostRequest(url, jsonString, headers, DifyKnowledgeBaseCreateVo.class);
    }

    @Override
    public Boolean del(String dataSetId) {
        checkKnowledgeBaseState();

        String url = buildRequestUrl("/v1/datasets/" + dataSetId, null);
        Map<String, String> headers = getAuthorizationHeaders();
        sendDeleteRequest(url, headers);

        return true;
    }

    @Override
    public DifyKnowledgeFileVo fileList(String dataSetId, String keyword, Integer page, Integer limit) {
        checkKnowledgeBaseState();

        // 构建请求参数
        Map<String, String> params = new HashMap<>();
        if (keyword != null) {
            params.put("keyword", keyword);
        }
        if (page != null) {
            params.put("page", String.valueOf(page));
        }
        if (limit != null) {
            params.put("limit", String.valueOf(limit));
        }

        String url = buildRequestUrl("/v1/datasets/" + dataSetId + "/documents", params);
        Map<String, String> headers = getAuthorizationHeaders();
        return sendGetRequest(url, headers, DifyKnowledgeFileVo.class);
    }

    @Override
    public DifyCreateByTextVo createByText(String datasetId, DifyCreateByTextBo bo) {
        checkKnowledgeBaseState();

        String url = buildRequestUrl("/v1/datasets/" + datasetId + "/document/create-by-text", null);
        Map<String, String> headers = getAuthorizationHeaders();
        String jsonString = org.apache.dubbo.common.utils.JsonUtils.toJson(bo);
        return sendPostRequest(url, jsonString, headers, DifyCreateByTextVo.class);
    }

    @Override
    public DifyCreateByFileVo createByFile(MultipartFile file, String fileName, String datasetId, DifyCreateByFileBo bo) {
        checkKnowledgeBaseState();
        String url = buildRequestUrl("/v1/datasets/" + datasetId + "/document/create-by-file", null);
        Map<String, String> headers = getAuthorizationHeaders();
        String jsonString = org.apache.dubbo.common.utils.JsonUtils.toJson(bo);
        // 使用 OkHttp 发送请求
        return sendPostRequestWithFile(url, jsonString, file, headers, DifyCreateByFileVo.class);
    }

    @Override
    public DifyCreateByFileVo createByFile(String storageId, String filName, String datasetId, DifyCreateByFileBo bo) {
        checkKnowledgeBaseState();
        String downloadUri = remoteFileService.selectUrlByIds(storageId);
        RemoteFile remoteFile = remoteFileService.selectById(storageId);
        MockMultipartFile file = null;
        try {
        URL url = new URL(downloadUri);
        java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
        InputStream stream = connection.getInputStream();
        if (stream == null)throw new RuntimeException("文件流为空！");
            file = new MockMultipartFile(remoteFile.getOriginalNameInfo(),stream);
        }catch (IOException e){
            log.error("文件流转换MultipartFile异常",e);
            throw new RuntimeException("文件流转换MultipartFile异常");
        }
//        MultipartFile multipartFile = FileUtils.changeFileName(file, remoteFile.getOriginalNameInfo());
        String finalName = StringUtils.isBlank(filName)?remoteFile.getOriginalName():filName;
        MultipartFile multipartFile = FileUtils.changeFileName(file, finalName+"."+remoteFile.getFileSuffix());

        String url = buildRequestUrl("/v1/datasets/" + datasetId + "/document/create-by-file", null);
        Map<String, String> headers = getAuthorizationHeaders();
        String jsonString = org.apache.dubbo.common.utils.JsonUtils.toJson(bo);
//        headers.put("Content-Type", "multipart/form-data");
        // 使用 OkHttp 发送请求
        return sendPostRequestWithFile(url, jsonString, multipartFile, headers, DifyCreateByFileVo.class);
    }

    @Override
    public DifyUpdateByTextVo updateByText(String datasetId, String documentId, DifyUpdateByTextBo bo) {
        checkKnowledgeBaseState();

        String url = buildRequestUrl("/v1/datasets/" + datasetId + "/documents/" + documentId + "/update-by-text", null);
        Map<String, String> headers = getAuthorizationHeaders();
        String jsonString = org.apache.dubbo.common.utils.JsonUtils.toJson(bo);
        return sendPostRequest(url, jsonString, headers, DifyUpdateByTextVo.class);
    }

    @Override
    public DifyUpdateByFileVo updateByFile(MultipartFile file, String datasetId, String documentId, DifyUpdateByFileBo bo) {
        checkKnowledgeBaseState();

        String url = buildRequestUrl("/v1/datasets/" + datasetId + "/documents/" + documentId + "/update-by-file", null);
        Map<String, String> headers = getAuthorizationHeaders();
        String jsonString = org.apache.dubbo.common.utils.JsonUtils.toJson(bo);

        // 使用 OkHttp 发送请求
        return sendPostRequestWithFile(url, jsonString, file, headers, DifyUpdateByFileVo.class);
    }

    @Override
    public DifyIndexingStatusVo indexingStatus(String dataSetId, String batch) {
        checkKnowledgeBaseState();

        String url = buildRequestUrl("/v1/datasets/" + dataSetId + "/documents/" + batch + "/indexing-status", null);
        Map<String, String> headers = getAuthorizationHeaders();

        // 使用 OkHttp 发送请求
        return sendGetRequest(url, headers, DifyIndexingStatusVo.class);
    }

    @Override
    public Boolean delFile(String datasetId, String documentId) {
        checkKnowledgeBaseState();

        String url = buildRequestUrl("/v1/datasets/" + datasetId + "/documents/" + documentId, null);
        Map<String, String> headers = getAuthorizationHeaders();
        sendDeleteRequest(url, headers);

        return true;
    }

    @Override
    public DifySegmentsVo segments(String datasetId, String documentId, DifySegmentsBo bo) {
        checkKnowledgeBaseState();

        String url = buildRequestUrl("/v1/datasets/" + datasetId + "/documents/" + documentId + "/segments", null);
        Map<String, String> headers = getAuthorizationHeaders();
        String jsonString = org.apache.dubbo.common.utils.JsonUtils.toJson(bo);
        return sendPostRequest(url, jsonString, headers, DifySegmentsVo.class);
    }

    @Override
    public DifySegmentsVo segments(String datasetId, String documentId, String keyword, String status) {
        checkKnowledgeBaseState();

        // 构建请求参数
        Map<String, String> params = new HashMap<>();
        if (keyword != null) {
            params.put("keyword", keyword);
        }
        if (status != null) {
            params.put("status", status);
        }

        String url = buildRequestUrl("/v1/datasets/" + datasetId + "/documents/" + documentId + "/segments", params);
        Map<String, String> headers = getAuthorizationHeaders();
        return sendGetRequest(url, headers, DifySegmentsVo.class);
    }

    @Override
    public Boolean segments(String datasetId, String documentId, String segmentId) {
        checkKnowledgeBaseState();

        String url = buildRequestUrl("/v1/datasets/" + datasetId + "/documents/" + documentId + "/segments/" + segmentId, null);
        Map<String, String> headers = getAuthorizationHeaders();
        sendDeleteRequest(url, headers);

        return true;
    }

    @Override
    public DifySegmentsVo segments(String datasetId, String documentId, String segmentId, DifyUpdateSegmentsBo bo) {
        checkKnowledgeBaseState();

        String url = buildRequestUrl("/v1/datasets/" + datasetId + "/documents/" + documentId + "/segments/" + segmentId, null);
        Map<String, String> headers = getAuthorizationHeaders();
        String jsonString = org.apache.dubbo.common.utils.JsonUtils.toJson(bo);
        return sendPostRequest(url, jsonString, headers, DifySegmentsVo.class);
    }

    @Override
    public DifyRetrieveVo retrieve(String datasetId, DifyRetrieveBo bo) {
        checkKnowledgeBaseState();

        String url = buildRequestUrl("/v1/datasets/" + datasetId + "/retrieve", null);
        Map<String, String> headers = getAuthorizationHeaders();
        String jsonString = org.apache.dubbo.common.utils.JsonUtils.toJson(bo);
        return sendPostRequest(url, jsonString, headers, DifyRetrieveVo.class);
    }

    @Override
    public Flux<String> chat(DifyChatBo bo) {
        checkKnowledgeBaseState();

        String url = buildRequestUrl("/v1/chat-messages", null);

        // 构造请求体
        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("inputs", new HashMap<>()); // 空的 inputs 对象
        requestBody.put("query", bo.getQuery());
        requestBody.put("response_mode", "streaming");
        requestBody.put("conversation_id", bo.getConversationId() != null ? bo.getConversationId() : "");
        requestBody.put("user", "abc-123");
        requestBody.put("files", new ArrayList<>()); // 空数组

        String jsonString = null;
        // 使用 Jackson 转换对象为 JSON 字符串
        jsonString = JsonUtils.toJsonString(requestBody);
        System.out.println("请求体: " + jsonString);

        return webClient.post()
            .uri(url)
            .header("Authorization", "Bearer " + bo.getApiKey())
            .contentType(MediaType.APPLICATION_JSON)
            .accept(MediaType.TEXT_EVENT_STREAM)
            .bodyValue(jsonString)
            .retrieve()
            .bodyToFlux(String.class)
            .doOnError(ex -> {
                // 打印详细错误信息
                System.out.println("WebClient 请求发生错误: " + ex.getMessage());
                if (ex instanceof WebClientResponseException) {
                    WebClientResponseException responseException = (WebClientResponseException) ex;
                    // 打印响应状态码和响应体
                    System.out.println("响应状态码: " + responseException.getRawStatusCode());
                    System.out.println("响应体: " + responseException.getResponseBodyAsString());
                } else if (ex instanceof WebClientRequestException) {
                    WebClientRequestException requestException = (WebClientRequestException) ex;
                    // 打印请求异常的信息
                    System.out.println("请求异常: " + requestException.getMessage());
                }
            })
            .onErrorResume(ex -> {
                // 如果发生错误，可以返回空的 Flux 或者其他默认值
                return Flux.empty(); // 可以根据需求做更复杂的错误处理
            });
    }
}
